package pool;

import com.hyacinth.utils.JDBCUtils;
import com.hyacinth.utils.StringUtils;
import config.DBPoolConfig;
import connection.PhysicalConnectionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.Vector;

/**
 * 连接池
 *
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public class ConnectionPool {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionPool.class);

    protected volatile String validationQuery = null;
    protected volatile int validationQueryTimeout = -1;
    private int poolingCount = 0; // 池子中连接数

    protected Driver driver;

    /**
     * 连接池配置
     **/
    private DBPoolConfig poolConfig;

    public ConnectionPool(DBPoolConfig config) {
        this.poolConfig = config;
    }

    /**
     * 空闲连接集合
     **/
    private Vector<PhysicalConnectionInfo> connections;

    /**
     * 活跃的连接集合
     **/
    private Vector<PhysicalConnectionInfo> activeConn;

    /**
     * 当前连接
     **/
    private static ThreadLocal<Connection> currentConn = new ThreadLocal<>();

    public void initPool() throws SQLException {
        if (poolConfig == null) {
            LOGGER.error("pool config is empty!!!");
            return;
        }
        int maxActive = poolConfig.getMaxActiveConn();
        int minIdle = poolConfig.getMinIdle();
        int maxIdle = poolConfig.getMinIdle();
        int initSize = poolConfig.getInitConn();
        if (maxActive <= 0) {
            throw new IllegalArgumentException("illegal maxActive " + poolConfig.getMaxActiveConn());
        }
        if (maxActive < minIdle) {
            throw new IllegalArgumentException("illegal maxActive " + maxActive);
        }

        if (initSize > maxActive) {
            throw new IllegalArgumentException("illegal initialSize " + initSize + ", maxActive " + maxActive);
        }

        String jdbcUrl = poolConfig.getUrl();
        if (StringUtils.isBlank(jdbcUrl)) {
            throw new IllegalArgumentException("jdbcUrl is empty!!!");
        }

        String driverClass = poolConfig.getDriverName();
        if (driverClass != null) {
            driverClass = driverClass.trim();
        }

        // 创建数据库驱动
        if (this.driver == null) {
            if (StringUtils.isBlank(driverClass)) {
                driverClass = JDBCUtils.getDriverClassName(jdbcUrl);
            }
            driver = JDBCUtils.createDriver(driverClass);
        } else {
            if (StringUtils.isBlank(driverClass)) {
                driverClass = driver.getClass().getName();
            }
        }

        // 初始化连接池大小
        connections = new Vector<PhysicalConnectionInfo>(maxActive);
        activeConn = new Vector<PhysicalConnectionInfo>(maxActive);

        SQLException connectError = null;
        try {
            // init connections
            for (int i = 0; i < initSize; ++i) {
                PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                connections.add(pyConnectInfo);
                incrementPoolingCount();
            }
        } catch (SQLException ex) {
            LOGGER.error("init datasource error, url: " + jdbcUrl, ex);
            connectError = ex;
        }
        if (connectError != null && poolingCount == 0) {
            throw connectError;
        }
    }

    private PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
        return null;
    }

    /**
     * 验证链接
     *
     * @param conn 链接
     * @throws SQLException SQL异常
     */
    public void validateConnection(Connection conn) throws SQLException {
        String query = getValidationQuery();

        if (conn.isClosed()) {
            throw new SQLException("validateConnection: connection closed");
        }

        if (null != query) {
            Statement stmt = null;
            ResultSet rs = null;
            try {
                stmt = conn.createStatement();
                if (getValidationQueryTimeout() > 0) {
                    stmt.setQueryTimeout(getValidationQueryTimeout());
                }
                rs = stmt.executeQuery(query);
                if (!rs.next()) {
                    throw new SQLException("validationQuery didn't return a row");
                }
            } finally {
                JDBCUtils.close(rs);
                JDBCUtils.close(stmt);
            }
        }
    }


    public String getValidationQuery() {
        return validationQuery;
    }

    public void setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
    }

    public int getValidationQueryTimeout() {
        return validationQueryTimeout;
    }

    public void setValidationQueryTimeout(int validationQueryTimeout) {
        this.validationQueryTimeout = validationQueryTimeout;
    }

    private final void decrementPoolingCount() {
        poolingCount--;
    }
    private final void incrementPoolingCount() {
        poolingCount++;
    }
}
